package nl.infodation.emr.pricingservice.dao.impl;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import nl.infodation.emr.pricingservice.dao.DAOFactory;
import nl.infodation.emr.pricingservice.dao.DomainRuleDAO;
import nl.infodation.emr.pricingservice.data.DomainRuleSearchItem;
import nl.infodation.emr.pricingservice.entity.DomainRule;
import nl.infodation.emr.pricingservice.entity.Environment;
import nl.infodation.emr.pricingservice.ui.DomainRuleList;
import nl.infodation.emr.utility.Constants;
import nl.infodation.emr.utility.DateTimeUtil;

import org.hibernate.QueryException;

import com.googlecode.genericdao.search.SearchResult;

/**
 * @author SUCCESS\tungo
 *
 */
public class DomainRuleDAOImpl extends DAOImpl<DomainRule, Long> implements DomainRuleDAO {

	private static DomainRuleDAO instance;
	
	private DomainRuleDAOImpl() {
		super();
	}
	
	public static synchronized DomainRuleDAO getInstance() {
		if (instance == null) {
			instance = new DomainRuleDAOImpl();
		}
		return instance;
	}

	/* (non-Javadoc)
	 * @see nl.infodation.emr.pricingservice.controller.interfaces.IDomainRuleController#searchInPage(nl.infodation.emr.pricingservice.data.DomainRuleSearchItem, int, int)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public SearchResult<DomainRule> searchInPage(DomainRuleSearchItem searchData,
			int startPage, int pageSize) {
		SearchResult<DomainRule> searchResult = new SearchResult<>();	

		EntityManager entityManager = null;
		try {
			entityManager = createEntityManager(EM_FACTORY);
			
			StringBuilder conditions = new StringBuilder();
			addSearchConditions(searchData, conditions);			
			
			Query query = entityManager.createQuery(SEARCH_AND_PAGING_SQL + conditions.toString());
			searchInPageParams(searchData, query);
			
			if (startPage > 0) {
				query.setFirstResult((startPage - 1) * pageSize);
			} else {
				query.setFirstResult(1);
			}
			query.setMaxResults(pageSize);
			searchResult.setResult(query.getResultList());

			query = entityManager.createQuery(SEARCH_AND_COUNT_SQL + conditions.toString());
			searchInPageParams(searchData, query);
			searchResult.setTotalCount(Integer.valueOf(String.valueOf((query.getSingleResult()))));
		} catch (IllegalArgumentException | QueryException ex) {
			LOG.error("Error reading searchInPage.", ex);
		} finally {
			close(entityManager);
		}
		
		return searchResult;
	}

	/**
	 * @param searchData
	 * @param conditions
	 */
	private void addSearchConditions(DomainRuleSearchItem searchData,
			StringBuilder conditions) {
		if (searchData.getCountries() != null && searchData.getCountries().size() > 0) {
			conditions.append(" AND p.country IN :countries");
		}			
		if (searchData.getFromDate() != null) {
			conditions.append(" AND (p.endDate >= '" + DateTimeUtil.getSqlDate(searchData.getFromDate()) + "' OR p.endDate IS NULL)");
		}
		if (searchData.getToDate() != null) {
			conditions.append(" AND (p.endDate <= '" + DateTimeUtil.getSqlDate(searchData.getToDate()) + "' OR p.endDate IS NULL)");
		}
		
		if (searchData.getTodayDate() != null) {
			conditions.append(" AND (p.endDate >= '" + DateTimeUtil.getSqlDate(searchData.getTodayDate()) + "' OR p.endDate IS NULL)");
		}
	}

	/**
	 * @param searchData
	 * @param query
	 */
	private void searchInPageParams(DomainRuleSearchItem searchData, Query query) {
		equalsOrEmpty(query, "busLine", searchData.getBusLine());
		equalsOrEmpty(query, "promotionValue", searchData.getPromoCode());
		likeOrEmpty(query, "ruleName", searchData.getRuleName());
		equalsOrEmpty(query, "country", searchData.getCountry());
		likeOrEmpty(query, "languageValue", searchData.getLanguage());
		query.setParameter("environmentId", searchData.getEnvironmentId());
		query.setParameter("promotionActive", searchData.getPromotionActive());
		
		if (searchData.getCountries() != null && searchData.getCountries().size() > 0) {
			query.setParameter("countries", searchData.getCountries());
		}	
	}
	
	/* (non-Javadoc)
	 * @see nl.infodation.emr.pricingservice.dao.DomainRuleDAO#searchIDs(nl.infodation.emr.pricingservice.data.DomainRuleSearchItem)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Long> findIDs(DomainRuleSearchItem searchData) {
		List<Long> result = new ArrayList<>();	

		EntityManager entityManager = null;
		try {
			entityManager = createEntityManager(EM_FACTORY);
			
			StringBuilder conditions = new StringBuilder();
			addSearchConditions(searchData, conditions);			
			
			Query query = entityManager.createQuery(SEARCH_IDS_SQL + conditions.toString());
			searchInPageParams(searchData, query);
			result = query.getResultList();
		} catch (IllegalArgumentException | QueryException ex) {
			LOG.error("Error reading findIDs.", ex);
		} finally {
			close(entityManager);
		}
		
		return result;
	}

	/* (non-Javadoc)
	 * @see nl.infodation.emr.pricingservice.dao.DomainRuleDAO#findByBusLine(java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<DomainRule> findByBusLine(String busLine) {
		List<DomainRule> list = new ArrayList<>();

		EntityManager entityManager = null;
		try {
			entityManager = createEntityManager(EM_FACTORY);
			Query query = entityManager.createQuery(FIND_BY_BUS_LINE_SQL);
			equalsOrEmpty(query, "busLine", busLine);
			list = query.getResultList();
		} catch (IllegalArgumentException | QueryException ex) {
			LOG.error("Error reading findByBusLine.", ex);
		} finally {
			close(entityManager);
		}
		
		return list;
	}

	/* (non-Javadoc)
	 * @see nl.infodation.emr.pricingservice.dao.DomainRuleDAO#findByBusLine(java.lang.String, java.lang.Integer)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<DomainRule> findByBusLine(String busLine, Integer environmentId) {
		List<DomainRule> list = new ArrayList<>();

		EntityManager entityManager = null;
		try {
			entityManager = createEntityManager(EM_FACTORY);
			Query query = entityManager.createQuery(FIND_BY_BUS_LINE_AND_ENV_SQL);
			equalsOrEmpty(query, "busLine", busLine);
			query.setParameter("environmentId", environmentId);
			list = query.getResultList();
		} catch (IllegalArgumentException | QueryException ex) {
			LOG.error("Error reading findByBusLine.", ex);
		} finally {
			close(entityManager);
		}
		
		return list;
	}

	/* (non-Javadoc)
	 * @see nl.infodation.emr.pricingservice.dao.DomainRuleDAO#findByName(java.lang.String, java.lang.Long)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<DomainRule> findByName(String name, Integer environmentId) {
		List<DomainRule> list = new ArrayList<>();

		EntityManager entityManager = null;
		try {
			entityManager = createEntityManager(EM_FACTORY);
			Query query = entityManager.createQuery(FIND_BY_NAME_AND_ENV_SQL);
			equalsOrEmpty(query, "ruleName", name);
			query.setParameter("environmentId", environmentId);
			
			list = query.getResultList();
		} catch (IllegalArgumentException | QueryException ex) {
			LOG.error("Error reading findByName.", ex);
		} finally {
			close(entityManager);
		}
		
		return list;
	}
	
	/* (non-Javadoc)
	 * @see nl.infodation.emr.pricingservice.dao.DomainRuleDAO#findByName(java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<DomainRule> findByName(String name) {
		List<DomainRule> list = new ArrayList<>();

		EntityManager entityManager = null;
		try {
			entityManager = createEntityManager(EM_FACTORY);
			Query query = entityManager.createQuery(FIND_BY_NAME_SQL);
			equalsOrEmpty(query, "ruleName", name);
			//active domainRule
			query.setParameter("promotionActive", Boolean.TRUE);			
			
			list = query.getResultList();
		} catch (IllegalArgumentException | QueryException ex) {
			LOG.error("Error reading findByName.", ex);
		} finally {
			close(entityManager);
		}
		
		return list;
	}

	/* (non-Javadoc)
	 * @see nl.infodation.emr.pricingservice.dao.DomainRuleDAO#copyToEnvironment(java.lang.String, java.lang.String, nl.infodation.emr.pricingservice.entity.Environment, java.lang.Boolean, java.util.List)
	 */
	public Boolean copyToEnvironment(String superBusLine, String busLine, Environment env, Boolean clean, List<Long> IDs) {
		Boolean copied = Boolean.FALSE;
		
		if (env == null) {
			LOG.error("Error saving domain rule.");
		} else {
			EntityManager entityManager = null;
			try {
				entityManager = EM_FACTORY.createEntityManager();
				entityManager.getTransaction().begin();
				if (clean) {
					LOG.info("Cleaning rules in environment..." + env.getEnvironmentId());
					// Collect all rules for this business line and environment
					List<DomainRule> rules2Del = DAOFactory.getDomainRuleDAO().findByBusLine(superBusLine, env.getEnvironmentId());
					LOG.info(rules2Del.size() + " rules found in environment..." + env.getEnvironmentId());
					DAOFactory.getDomainRuleDAO().remove(rules2Del);
				}
				LOG.info("Copying rules to environement..." + env.getEnvironmentId());
				List<DomainRule> rules = findEntitiesByIDs(DomainRuleList.getActiveList());
				
				int counter = 0;
				for (DomainRule rule : rules) {
					// Process for role
					if (Constants.PROVEA_ROLE.equals(busLine)) {
						if (!rule.getBusLine().equals(Constants.PROVEA_ROLE)) {
							continue; // skip
						}
					} else if (Constants.AFM_ROLE.equals(busLine)) {
						if (!rule.getBusLine().equals(Constants.AFM_ROLE)) {
							continue; // skip
						}
					}
					// copy
					counter++;
					DomainRule domainRule = rule.copy();
					domainRule.setDomainRuleId(null);
					domainRule.setEnvironment(env);
					entityManager.persist(domainRule);
				}
				copied = Boolean.TRUE;
				entityManager.getTransaction().commit();
				LOG.info(counter + " rules copied to environement..." + env.getEnvironmentId());
			} catch (Exception ex) {
				LOG.error("Error copyToEnvironment", ex);
				LOG.info("Rolling back...");
				rollback(entityManager);
			} finally {
				close(entityManager);
			}
		}
		
		if (copied) {
			LOG.info("Domain rules are copied.");
		}
		return copied;
	}

}
